package com.zusmart.base.scanner.support;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.zusmart.base.scanner.ScannerResult;
import com.zusmart.base.util.StringUtils;

public class DefaultScanner extends AbstractScanner {

	public static final String SEPARATOR = "$#9527#$";

	@Override
	protected Set<ScannerResult> doScan(List<String> patterns) {
		Set<ScannerResult> results = new LinkedHashSet<ScannerResult>();
		String[] paths = StringUtils.toArray(System.getProperty("java.class.path"), File.pathSeparator, true, true);
		if (null != paths && paths.length > 0) {
			for (String path : paths) {
				this.doScan(new File(path), "", results);
			}
		}
		return results;
	}

	protected void doScan(File file, String packageName, Set<ScannerResult> result) {
		if (null == file || !file.exists() || !file.canRead()) {
			return;
		}
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			if (null != files && files.length > 0) {
				for (File f : files) {
					this.doScan(f, StringUtils.isBlank(packageName) ? f.getName() : String.format("%s%s%s", packageName, SEPARATOR, f.getName()), result);
				}
			}
		} else if (file.isFile()) {
			if (this.isClassFile(file.getPath())) {
				String className = this.formatClassName(StringUtils.replace(packageName, SEPARATOR, "."));
				if (!this.matchPath(true, className)) {
					return;
				}
				Class<?> clazz = this.classForName(className);
				if (null == clazz) {
					return;
				}
				ScannerResultForDirClass classResult = new ScannerResultForDirClass(file.getParentFile(), file, clazz);
				result.add(classResult);
			} else if (this.isJarFile(file)) {
				this.doScanJarFile(file, result);
			} else {
				String filePath = this.formatClassName(StringUtils.replace(packageName, SEPARATOR, "/"));
				if (!this.matchPath(false, filePath)) {
					return;
				}
				ScannerResultForDirResource resourceResult = new ScannerResultForDirResource(file.getParentFile(), file);
				result.add(resourceResult);
			}
		}
	}

	protected void doScanJarFile(File file, Set<ScannerResult> result) {
		try {
			JarFile jarFile = new JarFile(file);
			Enumeration<JarEntry> entires = jarFile.entries();
			while (entires.hasMoreElements()) {
				JarEntry jarEntry = entires.nextElement();
				if (jarEntry.isDirectory()) {
					continue;
				}
				String entryName = jarEntry.getName();
				String path = String.format("jar:file:/%s!/%s", jarFile.getName().replace("\\", "/"), entryName);
				if (this.isClassFile(entryName)) {
					String className = StringUtils.replace(this.formatClassName(entryName), "/", ".");
					if (!this.matchPath(true, className)) {
						continue;
					}
					Class<?> clazz = this.classForName(className);
					if (null == clazz) {
						continue;
					}
					ScannerResultForJarClass classResult = new ScannerResultForJarClass(jarFile, jarEntry, clazz, path);
					result.add(classResult);
				} else {
					if (!this.matchPath(false, entryName)) {
						continue;
					}
					ScannerResultForJarResource resourceResult = new ScannerResultForJarResource(jarFile, jarEntry, path);
					result.add(resourceResult);
				}
			}
		} catch (IOException e) {
			// IGNORE
		}
	}

	protected String formatClassName(String className) {
		if (className.endsWith(EXT_CLASS)) {
			return className.substring(0, className.length() - EXT_CLASS.length());
		}
		return className;
	}

}